package handler

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
	"myai/core"
	"myai/core/types"
	"myai/service"
	"myai/service/dalle"
	"myai/service/oss"
	"myai/store/model"
	"myai/store/vo"
	"myai/utils"
	"myai/utils/resp"
)

type DallJobHandler struct {
	BaseHandler
	dallService *dalle.Service
	uploader    *oss.UploaderManager
	userService *service.UserService
}

func NewDallJobHandler(app *core.AppServer, db *gorm.DB, service *dalle.Service, manager *oss.UploaderManager,
	userService *service.UserService) *DallJobHandler {
	return &DallJobHandler{
		dallService: service,
		uploader:    manager,
		userService: userService,
		BaseHandler: BaseHandler{
			App: app,
			DB:  db,
		},
	}
}

// preCheck 检查用户是否登录，并且检查用户算力是否足够
func (h *DallJobHandler) preCheck(c *gin.Context) bool {
	user, err := h.GetLoginUser(c)
	if err != nil {
		resp.NotAuth(c)
		return false
	}

	if user.Power < h.App.SysConfig.DallPower {
		resp.ERROR(c, "当前用户剩余算力不足以完成本次绘画！")
		return false
	}
	return true
}

// Image godoc
// @Summary      创建一个绘画任务
// @Description  创建一个绘画任务
// @Tags         前台-DALL绘画
// @Accept       json
// @Produce      json
// @Param        req   body     types.DallTask  true  "绘画参数"
// @Success      200  {object}  types.BizVo{}
// @Router       /api/dall/image [post]
// @Security ApiKeyAuth
func (h *DallJobHandler) Image(c *gin.Context) {
	var data types.DallTask
	if err := c.ShouldBindJSON(&data); err != nil || data.Prompt == "" {
		resp.ERROR(c, types.InvalidArgs)
		return
	}

	var chatModel model.ChatModel
	if res := h.DB.Where("id = ?", data.ModelId).First(&chatModel); res.Error != nil {
		resp.ERROR(c, "模型不存在："+res.Error.Error())
		return
	}

	// 检查用户剩余算力
	user, err := h.GetLoginUser(c)
	if err != nil {
		resp.NotAuth(c)
		return
	}

	if user.Power < chatModel.Power {
		resp.ERROR(c, "当前用户剩余算力不足以完成本次绘画！")
		return
	}

	// 获取用户Id
	idValue, _ := c.Get(types.LoginUserID)
	userId := utils.IntValue(utils.InterfaceToString(idValue), 0)

	task := types.DallTask{
		ClientId:         data.ClientId,
		UserId:           uint(userId),
		ModelId:          chatModel.Id,
		ModelName:        chatModel.Value,
		Prompt:           data.Prompt,
		Quality:          data.Quality,
		Size:             data.Size,
		Style:            data.Style,
		Power:            chatModel.Power,
		TranslateModelId: h.App.SysConfig.TranslateModelId,
	}

	job := model.DallJob{
		UserId:   uint(userId),
		Prompt:   data.Prompt,
		Power:    chatModel.Power,
		TaskInfo: utils.JsonEncode(task),
	}

	res := h.DB.Create(&job)
	if res.Error != nil {
		resp.ERROR(c, "保存Dall任务到数据库失败："+res.Error.Error())
		return
	}

	task.Id = job.Id
	// 推送任务
	h.dallService.PushTask(task)

	// 扣减算力
	err = h.userService.DecreasePower(int(user.Id), chatModel.Power, model.PowerLog{
		Type:   types.PowerConsume,
		Model:  chatModel.Value,
		Remark: fmt.Sprintf("绘画提示词：%s", utils.CutWords(task.Prompt, 10)),
	})
	if err != nil {
		resp.ERROR(c, "绘画扣减算力失败: "+err.Error())
		return
	}
	resp.SUCCESS(c)
}

// ImgWall godoc
// @Summary      获取照片墙
// @Description  获取照片墙
// @Tags         前台-DALL绘画
// @Accept       json
// @Produce      json
// @Param        page        query     int  true  "页码"
// @Param        page_size   query     int  true  "每页数量"
// @Success      200  {object}  types.BizVo{data=vo.Page}
// @Router       /api/dall/imgWall [get]
// @Security ApiKeyAuth
func (h *DallJobHandler) ImgWall(c *gin.Context) {
	page := h.GetInt(c, "page", 0)
	pageSize := h.GetInt(c, "page_size", 0)
	err, jobs := h.getData(true, 0, page, pageSize, true)
	if err != nil {
		resp.ERROR(c, "获取照片墙数据失败："+err.Error())
		return
	}
	resp.SUCCESS(c, jobs)
}

// JobList godoc
// @Summary      获取 dall 任务列表
// @Description  获取 dall 任务列表
// @Tags         前台-DALL绘画
// @Accept       json
// @Produce      json
// @Param        page        query     int  true  "页码"
// @Param        page_size   query     int  true  "每页数量"
// @Param        finish   	 query     bool true  "任务是否完成"
// @Param        publish   	 query     bool true  "任务是否发布"
// @Success      200  {object}  types.BizVo{data=vo.Page}
// @Router       /api/dall/jobs [get]
// @Security ApiKeyAuth
func (h *DallJobHandler) JobList(c *gin.Context) {
	finish := h.GetBool(c, "finish")
	userId := h.GetLoginUserId(c)
	page := h.GetInt(c, "page", 0)
	pageSize := h.GetInt(c, "page_size", 0)
	publish := h.GetBool(c, "publish")

	err, jobs := h.getData(finish, userId, page, pageSize, publish)
	if err != nil {
		resp.ERROR(c, "获取DALL任务列表失败："+err.Error())
		return
	}
	resp.SUCCESS(c, jobs)
}

// Remove godoc
// @Summary      删除任务
// @Description  删除任务
// @Tags         前台-DALL绘画
// @Accept       json
// @Produce      json
// @Param        id        query     int  true  "任务Id"
// @Success      200  {object}  types.BizVo{}
// @Router       /api/dall/remove [get]
// @Security ApiKeyAuth
func (h *DallJobHandler) Remove(c *gin.Context) {
	id := h.GetInt(c, "id", 0)
	userId := h.GetLoginUserId(c)
	var job model.DallJob
	if res := h.DB.Where("id = ? and user_id = ?", id, userId).First(&job); res.Error != nil {
		resp.ERROR(c, "记录不存在")
		return
	}

	// 删除任务
	err := h.DB.Delete(&job).Error
	if err != nil {
		resp.ERROR(c, "删除DALL任务失败："+err.Error())
		return
	}

	// 删除任务生成的图片
	err = h.uploader.GetUploadHandler().Delete(job.ImgURL)
	if err != nil {
		log.Errorf("删除DALL任务图片失败：" + err.Error())
		return
	}
	resp.SUCCESS(c)
}

// Publish godoc
// @Summary      发布/取消发布图片到画廊显示
// @Description  发布/取消发布图片到画廊显示
// @Tags         前台-DALL绘画
// @Accept       json
// @Produce      json
// @Param        id        	 query     int  true  "任务Id"
// @Param        action   	 query     bool true  "任务是否发布"
// @Success      200  {object}  types.BizVo{}
// @Router       /api/dall/publish [get]
// @Security ApiKeyAuth
func (h *DallJobHandler) Publish(c *gin.Context) {
	id := h.GetInt(c, "id", 0)
	userId := h.GetLoginUserId(c)
	// 发布动作：true：发布 false：取消发布
	action := h.GetBool(c, "action")

	err := h.DB.Model(&model.DallJob{Id: uint(id), UserId: userId}).UpdateColumn("publish", action).Error
	if err != nil {
		resp.ERROR(c, "数据库操作失败："+err.Error())
		return
	}
	resp.SUCCESS(c)
}

// GetModels godoc
// @Summary      获取绘图模型
// @Description  获取绘图模型
// @Tags         前台-DALL绘画
// @Accept       json
// @Produce      json
// @Success      200  {object}  types.BizVo{data=[]vo.ChatModel}
// @Router       /api/dall/models [get]
// @Security ApiKeyAuth
func (h *DallJobHandler) GetModels(c *gin.Context) {
	var models []model.ChatModel
	err := h.DB.Where("type", "img").Where("enabled", true).Find(&models).Error
	if err != nil {
		resp.ERROR(c, "操作数据库出现错误："+err.Error())
		return
	}
	var modelVos []vo.ChatModel
	for _, v := range models {
		var modelVo vo.ChatModel
		err := utils.CopyObject(v, &modelVo)
		if err != nil {
			continue
		}
		modelVo.Id = v.Id
		modelVos = append(modelVos, modelVo)
	}
	resp.SUCCESS(c, modelVos)
}

func (h *DallJobHandler) getData(finish bool, userId uint, page int, pageSize int, publish bool) (error, vo.Page) {
	session := h.DB.Session(&gorm.Session{})
	if finish {
		session = session.Where("progress >= ?", 100).Order("id desc")
	} else {
		session = session.Where("progress < ?", 100).Order("id asc")
	}

	if userId > 0 {
		session = session.Where("user_id = ?", userId)
	}

	if publish {
		session = session.Where("publish", publish)
	}

	if page > 0 && pageSize > 0 {
		offset := (page - 1) * pageSize
		session = session.Offset(offset).Limit(pageSize)
	}

	// 统计总数
	var total int64
	session.Model(&model.DallJob{}).Count(&total)

	var items []model.DallJob
	res := session.Find(&items)
	if res.Error != nil {
		return res.Error, vo.Page{}
	}

	var jobs = make([]vo.DallJob, 0)
	for _, item := range items {
		var job vo.DallJob
		err := utils.CopyObject(item, &job)
		if err != nil {
			log.Errorf("拷贝数据出现错误：%v", err)
			continue
		}
		jobs = append(jobs, job)
	}
	return nil, vo.NewPage(total, page, pageSize, jobs)
}
